package com.kepler.social.web.configuration;

import com.beust.jcommander.internal.Lists;
import com.kepler.social.common.constants.BusinessState;
import com.kepler.social.common.utils.Jackson2Helper;
import com.kepler.social.web.comp.JwtAuthenticationEntryPoint;
import com.kepler.social.web.comp.LoginValidateAuthenticationProvider;
import com.kepler.social.web.comp.MobileAuthenticationProvider;
import com.kepler.social.web.comp.SSOLogoutSuccessHandler;
import com.kepler.social.web.filter.JwtAuthenticationTokenFilter;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Resource
    private LoginValidateAuthenticationProvider loginValidateAuthenticationProvider;
    @Resource
    private MobileAuthenticationProvider mobileAuthenticationProvider;
    @Resource
    private JwtAuthenticationEntryPoint point;
    @Resource
    private JwtAuthenticationTokenFilter filter;
    @Resource
    private SSOLogoutSuccessHandler logoutSuccessHandler;

    /**
     * //      https://stackoverflow.com/questions/74694980/authenticationmanager-authenticate-throws-java-lang-stackoverflowerror-null-w/74706573#74706573
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        List<AuthenticationProvider> myAuthenticationProviders = Lists.newArrayList(loginValidateAuthenticationProvider,
                mobileAuthenticationProvider);
        return new ProviderManager(myAuthenticationProviders);

    }

//
//    @Bean
//    public AuthenticationManager authenticationManager(List<AuthenticationProvider> myAuthenticationProviders) {
//        return new ProviderManager(myAuthenticationProviders);
//    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
                .exceptionHandling(ex -> ex.authenticationEntryPoint(point)
                        .authenticationEntryPoint((req, resp, authException) -> {
                            String msg;
                            if (authException instanceof UsernameNotFoundException
                                    || authException instanceof DisabledException
                                    || authException instanceof LockedException
                                    || authException instanceof AccountExpiredException
                                    || authException instanceof CredentialsExpiredException
                                    || authException instanceof BadCredentialsException) {
                                msg = authException.getMessage();
                            } else {
                                msg = "授权失败,请登录";
                            }
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            Map<String, Object> map = new HashMap<>();
                            map.put("code", BusinessState.AUTHENTICATION_ERROR.code);
                            map.put("message", msg);
                            out.write(Jackson2Helper.toJsonString(map));
                            out.flush();
                            out.close();
                        }).accessDeniedHandler((request, response, accessDeniedEx) -> {  // 认证访问 无权限
                            response.setContentType("application/json;charset=utf-8");
                            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                            PrintWriter out = response.getWriter();
                            Map<String, Object> map = new HashMap<>();
                            map.put("code", BusinessState.INSUFFICIENT_PERMISSION.code);
                            map.put("message", "权限不足");
                            out.write(Jackson2Helper.toJsonString(map));
                            out.flush();
                            out.close();
                        })
                ).authorizeHttpRequests((authorize) -> authorize
                        // 用户注册
                        .requestMatchers( "/file/**","/upload/**","/index", "/sec/sso-login", "/register/**", "/common/**")
                        .permitAll()
                        .anyRequest().authenticated())
                .logout((logout) -> logout.logoutUrl("/sec/logout").logoutSuccessHandler(logoutSuccessHandler).permitAll())
                .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        return http.build();
    }



}